package gui;

import com.healthmarketscience.jackcess.Database;
import com.healthmarketscience.jackcess.Table;
import database.DatabaseWorker;
import database.entity.Mail;
import gui.component.WorkProgress;
import gui.component.WorkStatus;
import java.io.IOException;
import java.util.Properties;
import javax.mail.Folder;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.NoSuchProviderException;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.mail.Store;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import javax.swing.JOptionPane;
import org.apache.commons.codec.binary.Base64;
import security.AESKey;
import security.Hash;
import security.RSAKeyPair;

/**
 *
 * @author Tom
 */
public class UICheckRegister extends javax.swing.JDialog 
{
    WorkProgress wp1;
    UIRegister register;    
    Store store;    
    Folder[] folders;    
    boolean successful;
    
    public UICheckRegister(UIRegister parent)
    {
        super(parent,true);
        initComponents();        
        this.setLocationRelativeTo(null);
        
        this.register=parent;
        store=null;        
        folders=null;
        successful=false;
        btNext.setVisible(false);
        revalidate();
        doWork();
    }
    
    private void doWork()
    {
        wp1 = new WorkProgress();
        wp1.addWork(() -> checkIncomingServer(), "S'identification avec le serveur");
        wp1.addWork(() -> getMailBoxes(), "Récuperation des boites");
        wp1.addWork(() -> sendTestEmail(), "Envoie d'un email d'essai");        
        wp1.addWork(() -> prepareDatabase(), "Création d'un répertoire dans la base de données");
        wp1.addWork(() -> finalizing(), "Génération du rapport");
        pnWork.add(wp1);        
        
        try 
        {
            wp1.start();            
        }
        catch (Exception ex)
        {
            JOptionPane.showMessageDialog(null, "Une erreur criticale s'est produite!");
            System.exit(2);
        }
    }
    
    private WorkStatus checkIncomingServer()
    {
        Properties props = System.getProperties();
        props.setProperty("mail.store.protocol", register.protocol);           
        try 
        {
                Session session = Session.getInstance(props, null);
                store = session.getStore();
        }
        catch(NoSuchProviderException ex)
        {   
            return new WorkStatus(WorkStatus.Status.FAIL, "Serveur d'entrée n'est pas trouvé");
        }
        
        try 
        {
            store.connect(register.incomingServer, register.email,register.password);
        } 
        catch (MessagingException ex)
        {
            return new WorkStatus(WorkStatus.Status.FAIL, "Connexion échouée au serveur d'entrée");
        }
        
        return new WorkStatus(WorkStatus.Status.SUCCESS, null);
    }
    
    private WorkStatus getMailBoxes()
    {
        if (wp1.getWork(1).result.status==WorkStatus.Status.FAIL)
        {
            return new WorkStatus(WorkStatus.Status.PASSED, null);
        }
        else
        {
            try 
            {
                folders = store.getDefaultFolder().list("*");             
            }
            catch (MessagingException ex)
            {
                return new WorkStatus(WorkStatus.Status.FAIL, "Accès aux boites de lettres échoué");
            }
        }
        return new WorkStatus(WorkStatus.Status.SUCCESS, null);
    }
    
    private WorkStatus sendTestEmail() 
    {
        if (wp1.getWork(1).result.status!=WorkStatus.Status.SUCCESS)
        {
            return new WorkStatus(WorkStatus.Status.PASSED, null);
        }
        else
        {
            //send message with SSL secure
            Properties props = new Properties();
            props.put("mail.smtp.host", "smtp.gmail.com");
            props.put("mail.smtp.socketFactory.port", "465");
            props.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");            
            props.put("mail.smtp.auth", "true");
            props.put("mail.smtp.port", "465");

            Session session = Session.getInstance(props,
                new javax.mail.Authenticator() 
                {
                    @Override
                    protected PasswordAuthentication getPasswordAuthentication()
                    {
                        //thunder pig test email
                        return new PasswordAuthentication("customer.thunderpig@gmail.com","!vinamilk100G!");
                    }
                });
            try 
            {
                Message message = new MimeMessage(session);            
                message.setRecipients(Message.RecipientType.TO,
                                InternetAddress.parse(register.email));

                message.setSubject("Welcome to ThunderPig");
                message.setText("Dear "+register.username+"," 
                        + "\n\nThis is a test mail sent from our mail service."
                        +"\nThank you for choosing ThunderPig."
                        +"\n\nSincerely,"
                        +"\nThunderPig development team.");

                Transport.send(message);
                return new WorkStatus(WorkStatus.Status.SUCCESS, null);
            }        
            catch (MessagingException ex)
            {
                return new WorkStatus(WorkStatus.Status.FAIL, "Serveur de sortie n'est pas trouvé");
            }
        }
    }    
    
    private WorkStatus prepareDatabase() 
    {        
        if (wp1.getWork(2).result.status!=WorkStatus.Status.SUCCESS || wp1.getWork(3).result.status!=WorkStatus.Status.SUCCESS)
        {
            return new WorkStatus(WorkStatus.Status.PASSED, null);
        }
        else
        {  
            Base64 base64 = new Base64();
            RSAKeyPair myKey;
            RSAKeyPair encryptKey;
            AESKey aesKey;
            
            //gererate user's RSA keypair
            //read random seed
            String result=null;
            do
            {
                result = JOptionPane.showInputDialog(this, "Entrez quelles charactères aléatoires pour générer vos clés", "");
            }
            while(result==null);
            myKey = new RSAKeyPair(result);   

            //generate other keys for encrypt infomation, base on passphrase
            encryptKey = new RSAKeyPair(register.password);
            aesKey = new AESKey(result);  
            
            //create random seed to protect password from dictionary attack
            String seed = Hash.generateSHA256(result).substring(10);

            //try to add record to database
            Database db;
            try 
            {
                db = DatabaseWorker.getDatabase();
                
                //create record in table Login
                Table tbLogin = db.getTable("Login");     
                //null for auto-number field, row1[0] contains UserID
                Object[] row1 = tbLogin.addRow(null,register.username,register.email,
                        Hash.generateSHA256(register.password+seed),
                        myKey.getPublicKeyBase64(),
                        base64.encodeToString(aesKey.encode(myKey.getPrivateKeyEncoded())),
                        register.incomingServer, register.protocol,
                        register.outgoingServer,register.port,register.ssl,
                        base64.encodeToString(aesKey.encode(register.password.getBytes())), 
                        new java.util.Date(System.currentTimeMillis()),
                        5,
                        base64.encodeToString(encryptKey.encodeByPublicKey(aesKey.getKeyEncoded())),
                        seed);                                               
                
                //create records in table MailBox
                Table tbMailBox = db.getTable("MailBox");   
                Table tbEmail = db.getTable("Mail");
                //each folder received in step2 will be one box
                for (Folder folder : folders)                 
                    if ((folder.getType() & Folder.HOLDS_MESSAGES & ~Folder.HOLDS_FOLDERS) != 0)
                    {
                        folder.open(Folder.READ_ONLY);
                        Object[] box = tbMailBox.addRow(null,row1[0],folder.getFullName());
                        
                        //download all current message and add into this box
                        //box[0] contains the BoxID
                        
                        Message[] messages = folder.getMessages();
                        for (Message message : messages) 
                        {                            
                            Mail m = DatabaseWorker.getMailFromMessage(message);
                            //add a record to the mail table
                            tbEmail.addRow(null,box[0],m.From,m.To,m.CC,m.BCC,
                                    base64.encodeToString(aesKey.encode(m.Subject.getBytes())),
                                    base64.encodeToString(aesKey.encode(m.Content.getBytes())),
                                    m.SentDate,m.ReceivedDate,m.Signed,m.Encrypted,m.HeaderField1,m.HeaderField2,m.HeaderField3,m.Unread);
                        }
                        folder.close(true);
                    }
                
                //create record in table contact group
                Table tbContactGroup = db.getTable("ContactGroup");
                Object[] row2 = tbContactGroup.addRow(null,row1[0],"Other");
                
                //add thunderpig customer service email to TABLE CONTACT group Other
                //row2[0] contains groupID
                Table tbContact = db.getTable("Contact");
                tbContact.addRow(null,row2[0],"ThunderPig Customer Service","customer.thunderpig@gmail.com","");
            }
            catch (MessagingException ex)
            {
                return new WorkStatus(WorkStatus.Status.FAIL, "Ne peut pas lire emails");
            }
            catch (IOException ex)
            {                
                return new WorkStatus(WorkStatus.Status.FAIL, "Erreur avec la base de données");
            }
        }        
        return new WorkStatus(WorkStatus.Status.SUCCESS, null);
    }
    
    private WorkStatus finalizing()
    {
        btNext.setVisible(true);
        if (wp1.getWork(4).result.status==WorkStatus.Status.SUCCESS)
        {
            lbInfo.setText("Félicitations, votre compte est maintenant prêt!");
            btNext.setText("Se connecter");  
            successful=true;
        }
        else
        {
            String report = wp1.generateReport();
            lbInfo.setText(report);
            btNext.setText("Réessayer");
        }
        return new WorkStatus(WorkStatus.Status.SUCCESS, null);
    }
            
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        pnWork = new javax.swing.JPanel();
        lbInfo = new javax.swing.JLabel();
        btNext = new javax.swing.JButton();

        setDefaultCloseOperation(javax.swing.WindowConstants.DO_NOTHING_ON_CLOSE);
        setType(java.awt.Window.Type.UTILITY);

        pnWork.setBorder(javax.swing.BorderFactory.createEtchedBorder());
        pnWork.setLayout(new javax.swing.BoxLayout(pnWork, javax.swing.BoxLayout.LINE_AXIS));

        lbInfo.setText("Veuillez patienter...");
        lbInfo.setVerticalAlignment(javax.swing.SwingConstants.TOP);

        btNext.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                btNextActionPerformed(evt);
            }
        });

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addGroup(layout.createSequentialGroup()
                        .addComponent(pnWork, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                        .addContainerGap())
                    .addGroup(layout.createSequentialGroup()
                        .addComponent(lbInfo, javax.swing.GroupLayout.PREFERRED_SIZE, 235, javax.swing.GroupLayout.PREFERRED_SIZE)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 91, Short.MAX_VALUE)
                        .addComponent(btNext, javax.swing.GroupLayout.PREFERRED_SIZE, 113, javax.swing.GroupLayout.PREFERRED_SIZE)
                        .addContainerGap())))
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addComponent(pnWork, javax.swing.GroupLayout.PREFERRED_SIZE, 210, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
                    .addComponent(btNext, javax.swing.GroupLayout.PREFERRED_SIZE, 35, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(lbInfo, javax.swing.GroupLayout.PREFERRED_SIZE, 102, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addContainerGap())
        );

        pack();
    }// </editor-fold>//GEN-END:initComponents

    private void btNextActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btNextActionPerformed
        this.setVisible(false);
        if (successful)
        {
            UILogin log = new UILogin();
            log.setVisible(true);
            this.dispose();
            register.dispose();
        }
        else
        {
            register.setVisible(true);
            this.dispose();
        }
    }//GEN-LAST:event_btNextActionPerformed

    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JButton btNext;
    private javax.swing.JLabel lbInfo;
    private javax.swing.JPanel pnWork;
    // End of variables declaration//GEN-END:variables
}
